package p15_aop初体验;

import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

/**
 * spring的aop是如何使用jdk代理与cglib代理的呢？
 * 两个切面概念:
 * aspect:aop中通用的概念，多个 通知 + 切点的集合
 * advisor:更细粒度的切面，仅有一个通知 + 一个切点，aspect的底层
 * 这节模拟了advisor是怎么增强目标方法的
 */
public class _2_Advisor {
    public static void main(String[] args) {
        /**
         * 本节以advisor为例进行讲解
         * aspect就是基于advisor实现的
         * 使用下面几个步骤创建一个advisor
         */
        //1.准备切点(Pointcut接口的实现类)，切点能根据切点表达式匹配符合条件的目标方法(如何匹配切点的呢?下一章介绍)
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* foo())");

        //2.准备通知，MethodInterceptor本质是环绕通知，不要与cglib的MethodInterceptor弄混了，但功能都差不多，都会调用目标方法，并在前后添加一些增加逻辑
        MethodInterceptor advice = invocation -> {
            //前置增强(通知)
            System.out.println("before....");
            //调用目标方法
            Object proceed = invocation.proceed();
            //后置增强(通知)
            System.out.println("after...");
            return proceed;
        };

        //3.切点与通知结合 -> 切面(Advisor接口实现类)
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);

        /**
         * 4.创建代理,不用我们去使用jdk代理或cglib代理，ProxyFactory会帮我们创建代理，内部会选择jdk或cglib其中一种
         *   ProxyFactory的proxyTargetClass属性 = false
         *      目标实现了接口，使用jdk代理；没有实现接口，使用cglib代理
         *   ProxyFactory的proxyTargetClass属性 = true
         *      统一使用cglib代理
         */
        ProxyFactory factory = new ProxyFactory();
        Target t = new Target();
        //factory.setProxyTargetClass(true);    //设置proxyTargetClass
        factory.setInterfaces(t.getClass().getInterfaces());      //指定实现的接口(Target没有接口，就是个空列表)
        factory.setTarget(t);
        factory.addAdvisor(advisor);
        //得到ProxyFactory创建的代理类
        Target proxy = (Target) factory.getProxy();
        //该方法被增强了
        proxy.foo();
        //该方法没有被增强
        proxy.bar();
        /**
         * spring在创建代理是，会调用切点类的匹配方法，与目标类的方法进行逐个匹配，匹配成功就进行增强
         */

    }

    static class Target {
        public void foo() {
            System.out.println("目标的foo....");
        }

        public void bar() {
            System.out.println("目标的bar.....");
        }
    }
}
